﻿#include "FITKAcousticsVTKGraphObjectSelectSEANodes.h"
#include "FITKAcousticsVTKGraphActor.h"

// VTK
#include <vtkUnstructuredGrid.h>
#include <vtkPoints.h>
#include <vtkMapper.h>
#include <vtkGlyph3D.h>
#include <vtkCubeSource.h>
#include <vtkProperty.h>
#include <vtkProperty2D.h>
#include <vtkStringArray.h>
#include <vtkLabelHierarchy.h>
#include <vtkPointSetToLabelHierarchy.h>
#include <vtkTextProperty.h>
#include <vtkLabelPlacementMapper.h>
#include <vtkFreeTypeLabelRenderStrategy.h>
#include <vtkPointData.h>

// App
#include "FITK_Kernel/FITKAppFramework/FITKGlobalData.h"
#include "FITK_Kernel/FITKAppFramework/FITKAppFramework.h"

// Data
#include "FITK_Component/FITKAcousticsModel/FITKAcousticsDiscModel.h"
#include "FITK_Component/FITKAcousticsModel/FITKAcousticsSEAModel.h"
#include "FITK_Component/FITKAcousticsModel/FITKAcousticsDiscNodeList.h"
#include "FITK_Component/FITKAcousticsModel/FITKAbstractSEASubSys.h"

// Graph and filter
#include "FITKAcousticsVTKSharedDataManager.h"
#include "FITK_Interface/FITKVTKAlgorithm/FITKGraphActor.h"
#include "FITK_Interface/FITKVTKAlgorithm/FITKGraphActor2D.h"

namespace Exchange
{
    FITKAcousticsVTKGraphObjectSelectSEANodes::FITKAcousticsVTKGraphObjectSelectSEANodes()
        : FITKAcousticsVTKGraphObject3D(nullptr)
    {
        // Initialize.
        init();

        // Set the layer need to be rendered.
        setRenderLayer(2);

        // Save if the bounds are fixed or dynamic.
        m_hasFixedBounds = false;
    }

    FITKAcousticsVTKGraphObjectSelectSEANodes::~FITKAcousticsVTKGraphObjectSelectSEANodes()
    {
        // Delete pointers.
        deleteVtkObj(m_ugrid);
        deleteVtkObj(m_points);
        deleteVtkObj(m_cubeSource);
        deleteVtkObj(m_glyph);
        deleteVtkObj(m_textArray);
        deleteVtkObj(m_textProperty);
    }

    void FITKAcousticsVTKGraphObjectSelectSEANodes::init()
    {
        // Create the grid data for the glyph.
        m_ugrid = vtkUnstructuredGrid::New();
        m_points = vtkPoints::New();
        m_ugrid->SetPoints(m_points);

        // Create the source and glyph.
        //@{
        m_cubeSource = vtkCubeSource::New();
        m_cubeSource->SetCenter(0., 0., 0.);
        m_cubeSource->SetXLength(m_glyphSize);
        m_cubeSource->SetYLength(m_glyphSize);
        m_cubeSource->SetZLength(m_glyphSize);

        m_glyph = vtkGlyph3D::New();
        m_glyph->SetSourceConnection(m_cubeSource->GetOutputPort());
        m_glyph->SetInputData(m_ugrid);
        //@}

        // Create the text array.
        m_textArray = vtkStringArray::New();
        m_textArray->SetNumberOfComponents(1);
        m_textArray->SetName("_index_label_");
        m_ugrid->GetPointData()->AddArray(m_textArray);

        // Create text property.( Black Arial 20ft.)
        m_textProperty = vtkTextProperty::New();
        m_textProperty->SetBold(true);
        m_textProperty->SetFontSize(m_fontSize);
        m_textProperty->SetColor(1., 1., 0.);
        m_textProperty->SetFontFamilyToArial();

        // Create 3D glyph actor.
        //@{
        m_fActor = FITKAcousticsVTKGraphActor::New();
        m_fActor->setActorType(ActorType::OtherActor);
        m_fActor->setGraphObject(this);
        m_fActor->SetPickable(false);
        m_fActor->SetVisibility(false);
        m_fActor->GetProperty()->SetColor(1., 0., 0.);
        m_fActor->GetProperty()->SetEdgeColor(0., 0., 0.);
        m_fActor->GetProperty()->SetEdgeVisibility(true);
        m_fActor->GetProperty()->SetLineWidth(1.);

        m_fActor->setInputConnection(m_glyph->GetOutputPort());
        addActor(m_fActor);
        //@}

        // Create 2D text actor.
        //@{
        m_fActorText2D = FITKGraphActor2D::New();
        m_fActorText2D->setGraphObject(this);
        m_fActorText2D->SetPickable(false);
        m_fActorText2D->SetVisibility(false);
        m_fActorText2D->setActorType(ActorType2D::TextActor);

        // Text mapper and strategy.
        vtkSmartPointer<vtkLabelPlacementMapper> mapperText = vtkSmartPointer<vtkLabelPlacementMapper>::New();
        mapperText->UseDepthBufferOff();
        mapperText->SetShapeToNone();
        mapperText->SetStyleToOutline();
        //mapperText->SetOutputTraversedBounds(true);
        mapperText->UseUnicodeStringsOff();

        vtkSmartPointer<vtkFreeTypeLabelRenderStrategy> strategyText = vtkSmartPointer<vtkFreeTypeLabelRenderStrategy>::New();

        // Label hierarchy.
        vtkSmartPointer<vtkPointSetToLabelHierarchy> textData = vtkSmartPointer<vtkPointSetToLabelHierarchy>::New();
        textData->SetInputData(m_ugrid);
        textData->SetTargetLabelCount(1);
        textData->SetMaximumDepth(15);
        textData->SetLabelArrayName(m_textArray->GetName());
        textData->SetTextProperty(m_textProperty);
        textData->Update();

        // Set input connecton of label hierarchy and strategy.
        mapperText->SetInputConnection(textData->GetOutputPort());
        mapperText->SetRenderStrategy(strategyText);

        // Show all labels
        mapperText->SetPlaceAllLabels(true);
        mapperText->SetMaximumLabelFraction(1.);

        mapperText->SetIteratorType(vtkLabelHierarchy::FULL_SORT);
        mapperText->Update();

        m_fActorText2D->SetMapper(mapperText);
        addActor(m_fActorText2D);
        //@}
    }

    void FITKAcousticsVTKGraphObjectSelectSEANodes::setSelectData(QVector<int> & indice)
    {
        if (!m_ugrid || !m_points || !m_fActor)
        {
            return;
        }

        // Reset the the grid and indice array.
        resetVtkObj(m_ugrid);
        resetVtkObj(m_points);
        resetVtkObj(m_textArray);
        m_textArray->SetNumberOfValues(0);

        vtkPoints* seaPoints = FITKAcousticsVTKSharedDataManager::GetInstance()->getSEANodesPoints();
        if (!seaPoints)
        {
            return;
        }

        for (int i = 0 ; i < indice.count() ; i ++)
        {
            // Insert a point.
            m_points->InsertNextPoint(seaPoints->GetPoint(indice[i]));

            // Insert the text array.
            m_textArray->InsertNextValue(QStringToCharA(QString::number(i + 1)));
        }

        // Intialize actor.
        initActorProperties(m_fActor, FITKAcousticsVTKCommons::ModelSolid);
    }

    void FITKAcousticsVTKGraphObjectSelectSEANodes::setGlyphSize(double glyphSize)
    {
        if (m_cubeSource)
        {
            m_cubeSource->SetXLength(glyphSize);
            m_cubeSource->SetYLength(glyphSize);
            m_cubeSource->SetZLength(glyphSize);
        }

        // Save the size.
        m_glyphSize = glyphSize;
    }

    void FITKAcousticsVTKGraphObjectSelectSEANodes::setVisible(bool visibility)
    {
        if (m_fActor)
        {
            m_fActor->SetVisibility(visibility);
        }

        if (m_fActorText2D)
        {
            m_fActorText2D->SetVisibility(visibility);
        }
    }

    void FITKAcousticsVTKGraphObjectSelectSEANodes::setTransparent(bool isOn)
    {
        // Get the opacity.
        double opa = isOn ? 1. - FITKAcousticsVTKCommons::s_defaultTransparency : 1.;

        if (m_fActor)
        {
            m_fActor->GetProperty()->SetOpacity(opa);
        }
    }

    void FITKAcousticsVTKGraphObjectSelectSEANodes::setColor(QColor color)
    {
        // Set the actor color.
        if (m_fActor && color.isValid())
        {
            double color3[3]{ 0., 0., 0. };
            FITKAcousticsVTKCommons::QColorToDouble3(color, color3);
            m_fActor->GetProperty()->SetColor(color3);
        }
    }

    void FITKAcousticsVTKGraphObjectSelectSEANodes::clearData()
    {
        // Reset the indice array.
        resetVtkObj(m_ugrid);
        resetVtkObj(m_points);
        resetVtkObj(m_textArray);
        m_textArray->SetNumberOfValues(0);

        // Update the actor.
        if (m_fActor)
        {   
            m_fActor->SetVisibility(false);
            m_fActor->update();
        }

        // Update the actor.
        if (m_fActorText2D)
        {
            m_fActorText2D->SetVisibility(false);
            m_fActorText2D->update();
        }
    }

    void FITKAcousticsVTKGraphObjectSelectSEANodes::initActorProperties(vtkProp* actor, QVariant details)
    {
        if (!actor)
        {
            return;
        }

        // Set model actor properties.
        // Default show face and edge.
        FITKAcousticsVTKGraphActor* fActor = FITKAcousticsVTKGraphActor::SafeDownCast(actor);
        if (!fActor)
        {
            return;
        }

        // Red wireframe or point.
        fActor->setScalarVisibility(false);
        fActor->SetPickable(false);

        switch (details.toInt())
        {
        case FITKAcousticsVTKCommons::ModelVertex:
        case FITKAcousticsVTKCommons::MeshNode:
        {
            // Points.
            fActor->GetProperty()->SetRepresentation(0);
            break;
        }
        case FITKAcousticsVTKCommons::ModelEdge:
        case FITKAcousticsVTKCommons::MeshElement:
        {
            // Wireframe.
            fActor->GetProperty()->SetRepresentation(1);
            fActor->GetProperty()->SetEdgeVisibility(true);
            break;
        }
        case FITKAcousticsVTKCommons::ModelFace:
        case FITKAcousticsVTKCommons::ModelSolid:
        {
            // Surface.
            fActor->GetProperty()->SetRepresentation(2);
            fActor->GetProperty()->SetEdgeVisibility(true);
            break;
        }
        case FITKAcousticsVTKCommons::Others:
        default:
            return;
        }
    }

    Acoustics::FITKAcousticsDiscNodeList* FITKAcousticsVTKGraphObjectSelectSEANodes::getSEANodesData()
    {
        // Get the SEA-model data.
        Acoustics::FITKAcousticsDiscModel* discModel = FITKGLODATA->getMeshData<Acoustics::FITKAcousticsDiscModel>();
        if (!discModel)
        {
            return nullptr;
        }

        Acoustics::FITKAcousticsSEAModel* seaModel = discModel->getSEAModel();
        if (!seaModel)
        {
            return nullptr;
        }

        // Get the SEA nodes.
        Acoustics::FITKAcousticsDiscNodeList* seaNodes = discModel->getNodes();
        if (!seaNodes)
        {
            return nullptr;
        }

        return seaNodes;
    }
}   // namespace Exchange